﻿using System;
using LogNaga.Logs;
using System.IO;
using LogNaga.Configuration;

namespace LogNaga
{
	/// <summary>
	/// 日志组件
	/// </summary>
	public class Logger
	{
		private static readonly string basePath = AppDomain.CurrentDomain.BaseDirectory;
		private string LogName { get; set; }
		private LogLevel defaultLogLevel = LogLevel.Debug;
		private Log logWriter;
		private ExceptionFormatter exceptionFormatter;

		//默认的ExceptionFormatter
		private static readonly ExceptionFormatter defaultExceptionFormatter = ex =>
		{
			return ex.Message + "\n" + ex.StackTrace + "\n" + ex.Source + "\n";
		};

		/// <summary>
		/// 负责记录的<see cref="Log"/>实例
		/// </summary>
		public Log LogWriter
		{
			get
			{
				return logWriter;
			}
			set
			{
				if (value == null)
				{
					//value = NoLog.Instance;//默认不记录任何异常
					var logFilePath = Path.Combine(basePath, LoggingConfigurationSection.GetConfig().DirectoryName);
					if (!Directory.Exists(logFilePath))
					{
						Directory.CreateDirectory(logFilePath);
					}

					logFilePath = Path.Combine(logFilePath, this.LogName);

					//value = new ThreadSafeFileLog(logFilePath, ex => { return ex.Message + "\n" + ex.StackTrace + "\n" + ex.Source + "\n"; });
					value = new ThreadSafeFileLog(logFilePath, defaultExceptionFormatter);
				}
				logWriter = value;
			}
		}

		/// <summary>
		/// 记录的异常级别
		/// </summary>
		public LogLevel LogLevel
		{
			get
			{
				return defaultLogLevel;
			}
			set
			{
				defaultLogLevel = value;
			}
		}

		/// <summary>
		/// 将<see cref="Exception"/>格式化为<see cref="string"/>格式的方法
		/// </summary>
		public ExceptionFormatter ExceptionFormatter
		{
			get
			{
				if (exceptionFormatter == null)
					exceptionFormatter = defaultExceptionFormatter;

				return exceptionFormatter;
			}
			set
			{
				if (value == null)
					exceptionFormatter = defaultExceptionFormatter;
				else
					exceptionFormatter = value;
			}
		}

		#region .ctor

		/// <summary>
		/// 初始化<see cref="Logger"/>实例
		/// </summary>
		/// <param name="type"><see cref="Type"/>类型名称</param>
		public Logger(Type type)
			: this(type, null)
		{
		}

		/// <summary>
		/// 初始化<see cref="Logger"/>实例
		/// </summary>
		/// <param name="type"><see cref="Type"/>类型名称</param>
		/// <param name="log"><see cref="Log"/>log实例</param>
		public Logger(Type type, Log log)
			: this(type.FullName, log)
		{
		}

		/// <summary>
		/// 初始化<see cref="Logger"/>实例
		/// </summary>
		/// <param name="logName">日志名</param>
		public Logger(string logName)
			: this(logName, null)
		{
		}

		/// <summary>
		/// 初始化<see cref="Logger"/>实例
		/// </summary>
		/// <param name="logName">日志名</param>
		/// <param name="log"><see cref="Log"/>log实例</param>
		public Logger(string logName, Log log)
		{
			this.LogName = logName;
			this.LogWriter = log;
		}

		#endregion

		#region 具体实现

		/// <summary>
		/// 记录Fatal模式的异常
		/// </summary>
		/// <param name="ex"></param>
		public void Fatal(Exception ex)
		{
			this.WriteLog(ex, LogLevel.Fatal);
		}

		/// <summary>
		/// 记录Error模式的异常
		/// </summary>
		/// <param name="ex"></param>
		public void Error(Exception ex)
		{
			this.WriteLog(ex, LogLevel.Error);
		}

		#endregion

		#region Help methods

		/// <summary>
		/// 写入log
		/// </summary>
		/// <param name="ex"></param>
		/// <param name="logLevel"></param>
		private void WriteLog(Exception ex, LogLevel logLevel)
		{
			//TODO: 没权衡好是否应该在这里抛出ArgumentNullException
			if (ex == null)
				return;

			//检查要记录的Log级别是否在允许范围之内
			if (CheckLogLevelSetting(logLevel))
			{
				var error = new Error(ex, logLevel);

				////如果log是从TextLog继承而来的,则需要将异常序列化为字符串存储
				//if (this.logWriter is TextLog)
				//{
				//    error.ErrorMessage = this.ExceptionFormatter(ex);
				//}

				this.LogWriter.Write(error);
			}
		}

		/// <summary>
		/// 检查错误级别
		/// </summary>
		/// <param name="desireLevel">要记录的错误</param>
		/// <returns><see cref="bool"/>我</returns>
		private bool CheckLogLevelSetting(LogLevel desireLevel)
		{
			return (desireLevel >= LogLevel.Fatal && desireLevel <= LogLevel.Debug) && (desireLevel <= this.defaultLogLevel);
		}

		#endregion
	}
}
